/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.servlet;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.model.core.ContentImage;
import com.blandware.atleap.model.core.Role;
import com.blandware.atleap.service.core.ContentResourceManager;
import com.blandware.atleap.webapp.form.core.UploadContentResourceForm;
import com.blandware.atleap.webapp.util.core.GlobalProperties;
import com.blandware.atleap.webapp.util.core.ImageUtil;
import com.blandware.atleap.webapp.util.core.ResourceTypesManager;
import com.blandware.atleap.webapp.util.core.WebappConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.upload.FormFile;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.imageio.IIOImage;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

/**
 * <p>Servlet to view thumbnail of image content resource</p>
 * <p>You can pass <code>contentResourceId</code> parameter or servlet can use
 * attribute from session. If <code>contentResourceId</code>, file upload form
 * is tried to get image data.</p>
 * <p><a href="ThumbnailServlet.java.html"><i>View Source</i></a></p>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @version $Revision: 1.3 $ $Date: 2008/01/15 16:56:51 $
 * @web.servlet name="thumbnail" load-on-startup="1"
 * @web.servlet-mapping url-pattern="/servlet/thumbnail/*"
 */
public class ThumbnailServlet extends HttpServlet {

	/**
	 * Commons Logging instance.
	 */
	protected transient final Log log = LogFactory.getLog(ThumbnailServlet.class);

	public static final String THUMBNAIL_DEFAULT_WIDTH_KEY = "thumbnail.default.width";
	public static final String THUMBNAIL_DEFAULT_HEIGHT_KEY = "thumbnail.default.height";

	private ResourceTypesManager resourceTypesManager = null;
	private ApplicationContext ctx = null;

	/**
	 * Initializes the servlet.
	 */
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		resourceTypesManager = ResourceTypesManager.getInstance(getServletContext());
		ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
	}

	/**
	 * Destroys the servlet.
	 */
	public void destroy() {

	}

	/**
	 * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
	 *
	 * @param request  servlet request
	 * @param response servlet response
     * @throws java.io.IOException  if i/o exception occures
     * @throws javax.servlet.ServletException   if other exception occures
	 */
	protected void processRequest(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {

		Long contentResourceId = null;
		if ( request.getParameter("contentResourceId") != null ) {
            try {
                contentResourceId = Long.valueOf(request.getParameter("contentResourceId"));
            } catch (NumberFormatException e) {
                // maybe value is malformed: do nothing, so contentResourceId
                // will be null as if it's not specified neither in parameter
                // nor in request
            }
        } else if ( request.getAttribute("contentResourceId") != null ) {
			contentResourceId = (Long) request.getAttribute("contentResourceId");
		}

		ServletContext servletContext = request.getSession().getServletContext();

		int maxWidth = GlobalProperties.getInstance(servletContext).getInteger(THUMBNAIL_DEFAULT_WIDTH_KEY).intValue();
		int maxHeight = GlobalProperties.getInstance(servletContext).getInteger(THUMBNAIL_DEFAULT_HEIGHT_KEY).intValue();
		try {
			if ( request.getParameter("width") != null ) {
				maxWidth = Integer.valueOf(request.getParameter("width")).intValue();
			}
			if ( request.getParameter("height") != null ) {
				maxHeight = Integer.valueOf(request.getParameter("height")).intValue();
			}
		} catch ( Exception ex ) {
			//do nothing
		}

		UploadContentResourceForm uploadContentResourceForm = (UploadContentResourceForm) request.getSession().getAttribute(WebappConstants.UPLOAD_CONTENT_RESOURCE_FORM_KEY);
		FormFile file = null;
		if ( uploadContentResourceForm != null ) {
			file = uploadContentResourceForm.getFile();
		}

		String mimeType;
		String resourceType;
		byte[] imageData;

		if ( contentResourceId != null ) {
			ContentResourceManager contentResourceManager = (ContentResourceManager) ctx.getBean(Constants.CONTENT_RESOURCE_MANAGER_BEAN);
			ContentImage contentImage = contentResourceManager.retrieveContentImage(contentResourceId);

			if ( contentImage == null ) {
				String message = "No content resource with ID=" + contentResourceId + " could be found";
				if ( log.isErrorEnabled() ) {
					log.error(message);
				}
				throw new ServletException(message);
			}

			//check roles
			List roles = contentImage.getRoles();
			boolean granted = false;
			if ( roles != null && roles.size() > 0 ) {
				for ( int i = 0; i < roles.size(); i++ ) {
					Role role = (Role) roles.get(i);
					if ( request.isUserInRole(role.getName()) ) {
						if ( log.isDebugEnabled() ) {
							log.debug(" User '" + request.getRemoteUser() +
							        "' has role '" + role.getName() + "', granting access");
						}
						granted = true;
						break;
					}
				}
			} else {
				granted = true;
				if ( log.isDebugEnabled() ) {
					log.debug("Anonymous user granting access");
				}
			}

			if ( !granted ) {
				// The current user is not authorized for this action
				if ( log.isDebugEnabled() ) {
					log.debug(" User '" + request.getRemoteUser() +
					        "' does not have any required role, denying access");
				}
				response.sendError(HttpServletResponse.SC_FORBIDDEN);
				return;
			}

			imageData = contentImage.getResourceData().getData();
			mimeType = contentImage.getMimeType();
			resourceType = contentImage.getType();

			if ( log.isDebugEnabled() ) {
				log.debug("Showing thumbnail from DB with mimeType=" + mimeType);
			}

		} else if ( file != null && file.getFileName() != null && file.getFileName().trim().length() != 0 ) {

			mimeType = resourceTypesManager.getMimeTypeByFileName(file.getFileName());
			resourceType = resourceTypesManager.getResourceTypeByFileName(file.getFileName());
			imageData = file.getFileData();

			if ( log.isDebugEnabled() ) {
				log.debug("Showing thumbnail from session with mimeType=" + mimeType);
			}

		} else {
			String message = "There are neither contentResourceId parameter in request nor " + WebappConstants.UPLOAD_CONTENT_RESOURCE_FORM_KEY + " attribute in session, or parameter is not a valid ID";
			if ( log.isWarnEnabled() ) {
				log.warn(message);
			}
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}

		if ( !resourceType.equalsIgnoreCase(Constants.RESOURCE_TYPE_IMAGE) ) {
			String message = "Resource type must be image";
			if ( log.isErrorEnabled() ) {
				log.error(message);
			}
			throw new ServletException(message);
		}

		OutputStream out = response.getOutputStream();

		try {
			IIOImage source = ImageUtil.decodeImage(imageData, mimeType);
			IIOImage scaled = ImageUtil.scaleToRectangle(source, maxWidth, maxHeight);
			if ( source != scaled ) {
				ByteArrayOutputStream scaledStream = new ByteArrayOutputStream();
				try {
					ImageUtil.encodeImage(scaled, scaledStream, mimeType);
				} catch ( IllegalArgumentException ex ) {
					//cannot encode into original format, try to default
					mimeType = resourceTypesManager.getDefaultMimeType(resourceType);
					response.setContentType(mimeType);
					ImageUtil.encodeImage(scaled, scaledStream, mimeType);
				}
				imageData = scaledStream.toByteArray();
				scaledStream.close();
			}
		} catch ( IOException ex ) {
			if ( log.isErrorEnabled() ) {
				log.error("Cannot read image, maybe data corrupted", ex);
			}
		}

		response.addHeader("Cache-Control", "no-cache,no-store,max-age=0");
		response.addHeader("Pragma", "No-cache");
		response.setDateHeader("Expires", 1);
		response.setContentType(mimeType);
		response.setContentLength(imageData.length);
		out.write(imageData);

		out.close();
	}

	/**
	 * Handles the HTTP <code>GET</code> method.
	 *
	 * @param request  servlet request
	 * @param response servlet response
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Handles the HTTP <code>POST</code> method.
	 *
	 * @param request  servlet request
	 * @param response servlet response
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
	        throws ServletException, IOException {
		processRequest(request, response);
	}

	/**
	 * Returns a short description of the servlet.
	 */
	public String getServletInfo() {
		return "Servlet to view thumbnail of image content resource";
	}

}